package com.ikingtech.framework.sdk.limit.mvc;

import com.ikingtech.framework.sdk.limit.annotation.RateLimiter;
import com.ikingtech.framework.sdk.utils.Tools;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author zhangqiang
 */
@Slf4j
@RequiredArgsConstructor
public class MvcRateLimiterFactory implements ApplicationRunner {

    protected static final Map<String, RateLimitInfo> RATE_LIMIT_INFO_MAP = new HashMap<>();

    private final RequestMappingHandlerMapping requestMappingHandlerMapping;

    @Value("${spring.application.name}")
    private String name;

    public static Map<String, RateLimitInfo> getRateLimitInfoMap() {
        return RATE_LIMIT_INFO_MAP;
    }

    @Override
    public void run(ApplicationArguments args) {
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
        handlerMethods.forEach((k, v) -> {
            Set<String> patternValues = k.getPatternValues();
            RequestMethodsRequestCondition methodsCondition = k.getMethodsCondition();
            String firstRequestType = getFirstRequestType(methodsCondition.getMethods());
            String firstValue = getFirstValue(patternValues);
            String fullPath = Tools.Str.SLASH + name + firstValue;
            Method method = v.getMethod();
            RateLimiter annotation = method.getAnnotation(RateLimiter.class);
            if (annotation == null) {
                return;
            }
            int count = annotation.count();
            int time = annotation.time();
            RateLimitInfo rateLimitInfo = new RateLimitInfo();
            rateLimitInfo.setCount(count);
            rateLimitInfo.setTime(time);
            rateLimitInfo.setRequestType(firstRequestType);
            rateLimitInfo.setMapping(firstValue);
            rateLimitInfo.setFullMapping(fullPath);
            rateLimitInfo.setApplicationName(name);
            RATE_LIMIT_INFO_MAP.put(firstRequestType + firstValue, rateLimitInfo);
        });
    }

    private static String getFirstRequestType(Set<RequestMethod> methods) {
        if (Tools.Coll.isBlank(methods)) {
            return "";
        }
        return methods.stream().map(RequestMethod::name).findFirst().orElse("");
    }

    private String getFirstValue(Set<String> patternValues) {
        if (Tools.Coll.isBlank(patternValues)) {
            return "";
        }
        return patternValues.stream().findFirst().orElse("");
    }

}
